#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "ListeException.h"
////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::tradC(unsigned short indentation) {
    

    for (unsigned int i = 0; i < tabInst.size(); i++) {
        Noeud::tradC(indentation+1);
        tabInst[i]->tradC(indentation+1);
        cout << "; \n";
    }
}
////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::tradC(unsigned short indentation) {
    variable->tradC(indentation);
    cout << "=";
    expression->tradC(indentation);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBinaire::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "+") valeur = og + od;
    else if (this->operateur == "-") valeur = og - od;
    else if (this->operateur == "*") valeur = og * od;
    else if (this->operateur == "ou") valeur = og || od;
    else if (this->operateur == "et") valeur = og && od;
    else if (this->operateur == "<") valeur = og < od;
    else if (this->operateur == ">") valeur = og > od;
    else if (this->operateur == "<=") valeur = og <= od;
    else if (this->operateur == ">=") valeur = og >= od;
    else if (this->operateur == "==" || this->operateur == "non") valeur = og == od;
    else if (this->operateur == "!=") valeur = og != od;

    else // this->operateur=="/"
        if (od != 0)
        valeur = og / od;
    else {
        cout << "Erreur pendant l'interpretation : division par zero" << endl;
        exit(0); // plus tard on levera une exception
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::tradC(unsigned short indentation) {
    operandeGauche->tradC(indentation);
    cout << operateur.getChaine();
    operandeDroit->tradC(indentation);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(vector<NoeudInstCond*> tabInstSi, Noeud* instSinon = NULL) {
    this->m_tabInstSi = tabInstSi;
    this->m_instSinon = instSinon;

}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstSi::getValeur() {

    for (unsigned int i = 0; i < m_tabInstSi.size(); i++) {
        if (m_tabInstSi[i]->getCond()) return m_tabInstSi[i]->getValeur(); //si la condition est vrai alors on retourne la valeur de sa sequence
    }
    if (m_instSinon != NULL) //m_instSinon est NULL si il n y a pas de sinon
        return this->m_instSinon->getValeur(); //si il  ya un sinon, on  retourne la valeur de sa sequence 

    return 0;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficher(unsigned short indentation) {
    unsigned int i = 1;

    Noeud::afficher(indentation);
    cout << "Noeud - InstSi" << endl;
    m_tabInstSi[0]->afficher(indentation);
    if (m_tabInstSi.size() > 0) {
        while (i < m_tabInstSi.size()) {
            Noeud::afficher(indentation);
            cout << "Noeud - InstSinonSi" << endl;
            m_tabInstSi[i]->afficher(indentation);
            i++;
        }
    }
    if (m_instSinon != NULL) {
        Noeud::afficher(indentation);
        cout << "Noeud - InstSinon" << endl;
        m_instSinon->afficher(indentation + 1);
    }
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::tradC(unsigned short indentation) {
    unsigned int i = 1;
    cout << "if (";
    m_tabInstSi[0]->tradCond(indentation);
    cout << ") {\n";
    Noeud::tradC(indentation);
    m_tabInstSi[0]->tradInst(indentation);
    Noeud::tradC(indentation);
    cout << "}\n";
    Noeud::tradC(indentation);
    while (i < m_tabInstSi.size()) {
        cout << "else if (";
        m_tabInstSi[i]->tradCond(indentation);
        cout << ") {\n";
        Noeud::tradC(indentation);
        m_tabInstSi[i]->tradInst(indentation);
        Noeud::tradC(indentation);
        cout << "}\n";
        Noeud::tradC(indentation);
        i++;
    }
    cout << "else {\n";
    Noeud::tradC(indentation);
    m_instSinon->tradC(indentation);
    Noeud::tradC(indentation);
    cout << "}\n";
    Noeud::tradC(indentation);
}


////////////////////////////////////////////////////////////////////////////////
// NoeudInstCond
////////////////////////////////////////////////////////////////////////////////

NoeudInstCond::NoeudInstCond(Noeud* cond, Noeud* inst) {
    this->m_cond = cond;
    this->m_inst = inst;

}
////////////////////////////////////////////////////////////////////////////////

bool NoeudInstCond::getCond() {
    //getCond retourne le resultat de l'expression booléenne de l'instruction
    return m_cond->getValeur();

}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstCond::getValeur() {
    //getValeur retourne le resultat de la séquence de l'instruction

    return m_inst->getValeur();


}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstCond::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    m_cond->afficher(indentation + 1);
    m_inst->afficher(indentation + 1);

}

void NoeudInstCond::tradCond(unsigned short indentation) {

    m_cond->tradC(indentation + 1);

}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstCond::tradInst(unsigned short indentation) {

    m_inst->tradC(indentation + 1);

}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* cond, Noeud* inst) : NoeudInstCond::NoeudInstCond(cond, inst) {

}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstTq::getValeur() {
    int valeur = 0;
    while (NoeudInstCond::getCond()) {
        valeur = NoeudInstCond::getValeur();
    }
    return valeur;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << " Noeud - InstTq" << endl;
    NoeudInstCond::afficher(indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::tradC(unsigned short indentation) {

    cout << "while (";
    tradCond(indentation);
    cout << ") {\n";
    tradInst(indentation);
    Noeud::tradC(indentation);
    cout << "}\n";
    Noeud::tradC(indentation);
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* cond, Noeud* inst) : NoeudInstCond::NoeudInstCond(cond, inst) {

}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstRepeter::getValeur() {
    int valeur = 0;
    do {
        valeur = NoeudInstCond::getValeur();
    } while (!NoeudInstCond::getCond());
    return valeur;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << " Noeud - InstRepeter" << endl;
    NoeudInstCond::afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::tradC(unsigned short indentation) {

    cout << "do {\n";
    tradInst(indentation);
    cout << "} while (";
    tradCond(indentation);
    cout << ")";
    Noeud::tradC(indentation);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* ini, Noeud* cond, Noeud* inc, Noeud* seq) {
    m_ini = ini;
    m_cond = cond;
    m_inc = inc;
    m_seq = seq;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstPour::getValeur() {
    int valeur = 0;
    m_ini->getValeur();
    while (m_cond->getValeur()) {

        valeur = m_seq->getValeur();
        m_inc->getValeur();

    }

    return valeur;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - InstPour" << endl;
    m_ini->afficher(indentation + 1);
    m_cond->afficher(indentation + 1);
    m_inc->afficher(indentation + 1);
    m_seq->afficher(indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::tradC(unsigned short indentation) {

    cout << "for (";
    m_ini->tradC(indentation);
    cout << ";";
    m_cond->tradC(indentation);
    cout << ";";
    m_inc->tradC(indentation);
    cout << ") {\n";
    m_seq->tradC(indentation + 1);
    cout << "}";

}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* var, int val) {
    m_var = var;
    m_val = val;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstLire::getValeur() {
    cin >> m_val;
    ((SymboleValue*) m_var)->setValeur(m_val); // on affecte la variable
    return m_val;

}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - InstLire" << endl;
    m_var->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::tradC(unsigned short indentation) {

    cout << "cin >> ";
    m_var->tradC(indentation);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* exp) {
    m_exp = exp;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstEcrire::getValeur() {
    return m_exp->getValeur();  
    
}
////////////////////////////////////////////////////////////////////////////////


void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - InstEcrire" << endl;
    m_exp->afficher(indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::tradC(unsigned short indentation) {

    cout << "cout << " ;
    m_exp->tradC(indentation);
}


////////////////////////////////////////////////////////////////////////////////
// NoeudInstSwitch
////////////////////////////////////////////////////////////////////////////////

NoeudInstSwitch::NoeudInstSwitch(Noeud* var, vector<NoeudInstCond*> tabCas, vector<int> sortie) {
    this->m_tabCas = tabCas;
    this->m_var = var;
    this->m_Sortie = sortie;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstSwitch::getValeur() {

    for (unsigned int i = 0; i < m_tabCas.size(); i++) {
        if (m_tabCas[i]->getCond() == m_var->getValeur()) {

            return m_tabCas[i]->getValeur(); //si la valeur entree correspond a la valeur de condition(uniquement des int) on retourne la seq associé
        }

    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstSwitch::afficher(unsigned short indentation) {
    unsigned int i = 1;

    Noeud::afficher(indentation);
    cout << "Noeud - InstSwitch" << endl;
    m_tabCas[0]->afficher(indentation);
    if (m_tabCas.size() > 0) {
        while (i < m_tabCas.size()) {
            Noeud::afficher(indentation);
            cout << "Noeud - InstCas" << endl;
            m_tabCas[i]->afficher(indentation);
            i++;
        }
    }

}